/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fi.uba.algo3.tp3.modelo.actores;

import ar.uba.fi.algo3.titiritero.Posicionable;
import fi.uba.algo3.tp3.modelo.servicios.ServicioJuego;
import fi.uba.algo3.tp3.modelo.inanimados.Obstaculo;

/**
 *
 * @author Familia
 */
public class FantasmaBobo extends Fantasma {

    public FantasmaBobo(ServicioJuego servicio, Integer distanciaDeColision) {
        super(servicio, distanciaDeColision);
    }

    @Override
    /* Mueve el fantasma acercandose al pacman en forma aleatoria. Este metodo */
    /* asume que al invocarse el pacman y el fantasma están en distinta posicion. */
    protected void vivirAtacando(ServicioJuego servicio) {
        this.intentarLograrDistanciaDeseada(servicio, true);
    }

    @Override
    /* Mueve el fantasma huyendo del pacman. Este metodo asume que al invocarse */
    /* el pacman y el fantasma están en distinta posicion. */
    protected void vivirHuyendo(ServicioJuego servicio) {
        this.intentarLograrDistanciaDeseada(servicio, false);
    }

    @Override
    /* Mueve el fantasma en direccion a la jaula. */
    protected void vivirComido() {
        int distanciaHorizontal = this.getX() - posicionInicialX;
        int distanciaVertical = this.getY() - posicionInicialY;
        if (Math.abs(distanciaHorizontal) > Math.abs(distanciaVertical)) {
            moverHorizontalmente(distanciaHorizontal);
        } else {
            moverVerticalmente(distanciaVertical);
        }
        //cuando van a la jaula no importa si pasan por encima de los obstaculos
        this.x += sentidoX;
        this.y += sentidoY;
    }

    private void intentarLograrDistanciaDeseada (ServicioJuego servicio, boolean estaAtacando) {
        int sentido = 0;
        Pacman pacman = servicio.getPacman();
        int distanciaInicial = this.calcularDistanciaAlPosicionable(pacman);
        int distanciaFinal = distanciaInicial;
        while (!this.seLogroLaDistanciaDeseada(estaAtacando, distanciaInicial, distanciaFinal)) {
            sentido++;
            if (sentido > 1)
                this.revertirMovimiento();
            this.moverSegunSentido(sentido);
            this.x += sentidoX;
            this.y += sentidoY;
            distanciaFinal = this.calcularDistanciaAlPosicionable(pacman);
        }
        Obstaculo obstaculo = servicio.obtenerObstaculoMasCercano(this, distanciaDeColision.doubleValue());
        if (obstaculo != null){
            this.revertirMovimiento();
            MovimientosDelFantasmaBobo movimientos = new MovimientosDelFantasmaBobo (this,servicio);
            this.intentarOtrosMovimientos (sentido,movimientos);
        }
    }

    private void moverSegunSentido (int sentido) {
        switch (sentido) {
            case 1:
                this.cambiarSentidoAVerticalAbajo();
                break;
            case 2:
                this.cambiarSentidoAVerticalArriba();
                break;
            case 3:
                this.cambiarSentidoAHorizontalIzquierda();
                break;
            case 4:
                this.cambiarSentidoAHorizontalDerecha();
                break;
            default:
                break;
        }
    }

    private void intentarOtrosMovimientos(int sentidoQueFallo, MovimientosDelFantasmaBobo opciones) {
        switch (sentidoQueFallo) {
            case 1:
                opciones.hayObstaculoEnSentidoVerticalAbajo();
                break;
            case 2:
                opciones.hayObstaculoEnSentidoVerticalArriba();
                break;
            case 3:
                opciones.hayObstaculoEnSentidoHorizontalIzquierda();
                break;
            case 4:
                opciones.hayObstaculoEnSentidoHorizontalDerecha();
                break;
            default:
                break;
        }
    }

    private int calcularDistanciaAlPosicionable(Posicionable objeto) {
        int distanciaHorizontal = Math.abs(this.getX() - objeto.getX());
        int distanciaVertical = Math.abs(this.getY() - objeto.getY());
        return distanciaHorizontal + distanciaVertical;
    }

    private void revertirMovimiento() {
        this.x -= this.sentidoX;
        this.y -= this.sentidoY;
    }

    /* Cambia el sentido del movimiento del fantasma en cualquiera de las 4 */
    /* direcciones en forma aleatoria. */
    private int cambiarSentidoEnFormaAleatoria() {
        double opcionMovi = Math.random();
        int sentido;
        if (opcionMovi < 0.25) {
            sentido = 1;
        } else if ((opcionMovi >= 0.25) && (opcionMovi < 0.5)) {
            sentido = 2;
        } else if ((opcionMovi >= 0.5) && (opcionMovi < 0.75)) {
            sentido = 3;
        } else {
            sentido = 4;
        }
        return sentido;
    }

    private boolean seLogroLaDistanciaDeseada(boolean estaAtacando, int distInicial, int distActual) {
        if (estaAtacando) {
            return distInicial > distActual;
        } else {
            return distInicial < distActual;
        }
    }

    public void moverHorizontalmente(int distancia) {
        if (distancia > 0) {
            this.cambiarSentidoAHorizontalIzquierda();
        } else {
            this.cambiarSentidoAHorizontalDerecha();
        }
    }

    public void moverVerticalmente(int distancia) {
        if (distancia > 0) {
            this.cambiarSentidoAVerticalArriba();
        } else {
            this.cambiarSentidoAVerticalAbajo();
        }
    }

    private class MovimientosDelFantasmaBobo{
        
        FantasmaBobo fantasma;
        ServicioJuego servicio;
        
        public MovimientosDelFantasmaBobo (FantasmaBobo fantasma, ServicioJuego servicio){
            this.fantasma = fantasma;
            this.servicio = servicio;
        }
        
        public void hayObstaculoEnSentidoVerticalArriba (){
            Obstaculo obstaculo = this.probarSentidoHorizontalIzquierda(fantasma, servicio);
            if (obstaculo != null){
                fantasma.revertirMovimiento();
                obstaculo = this.probarSentidoVerticalAbajo(fantasma, servicio);
                if (obstaculo != null){
                    fantasma.revertirMovimiento();
                    this.probarSentidoHorizontalDerecha(fantasma, servicio);
                }
            }
        }

        public void hayObstaculoEnSentidoVerticalAbajo(){
            Obstaculo obstaculo = this.probarSentidoHorizontalDerecha(fantasma, servicio);
            if (obstaculo != null){
                fantasma.revertirMovimiento();
                obstaculo = this.probarSentidoHorizontalIzquierda(fantasma, servicio);
                if (obstaculo != null){
                    fantasma.revertirMovimiento();
                    this.probarSentidoVerticalArriba(fantasma, servicio);
                }
            }
        }
        
        public void hayObstaculoEnSentidoHorizontalDerecha(){
            Obstaculo obstaculo = this.probarSentidoVerticalArriba(fantasma, servicio);
            if (obstaculo != null){
                fantasma.revertirMovimiento();
                obstaculo = this.probarSentidoHorizontalIzquierda(fantasma, servicio);
                if (obstaculo != null){
                    fantasma.revertirMovimiento();
                    this.probarSentidoVerticalAbajo(fantasma, servicio);
                }
            }
        }

        public void hayObstaculoEnSentidoHorizontalIzquierda(){
            Obstaculo obstaculo = this.probarSentidoVerticalAbajo(fantasma, servicio);
            if (obstaculo != null){
                fantasma.revertirMovimiento();
                obstaculo = this.probarSentidoHorizontalDerecha(fantasma, servicio);
                if (obstaculo != null){
                    fantasma.revertirMovimiento();
                    this.probarSentidoVerticalArriba(fantasma, servicio);
                }
            }
        }

        private Obstaculo probarSentidoVerticalArriba (FantasmaBobo fantasma, ServicioJuego servicio){
            fantasma.cambiarSentidoAVerticalArriba();
            fantasma.x += fantasma.sentidoX;
            fantasma.y += fantasma.sentidoY;
            return servicio.obtenerObstaculoMasCercano(fantasma, fantasma.distanciaDeColision.doubleValue());
        }
        private Obstaculo probarSentidoVerticalAbajo (FantasmaBobo fantasma, ServicioJuego servicio){
            fantasma.cambiarSentidoAVerticalAbajo();
            fantasma.x += fantasma.sentidoX;
            fantasma.y += fantasma.sentidoY;
            return servicio.obtenerObstaculoMasCercano(fantasma, fantasma.distanciaDeColision.doubleValue());
        }
        private Obstaculo probarSentidoHorizontalDerecha (FantasmaBobo fantasma, ServicioJuego servicio){
            fantasma.cambiarSentidoAHorizontalIzquierda();
            fantasma.x += fantasma.sentidoX;
            fantasma.y += fantasma.sentidoY;
            return servicio.obtenerObstaculoMasCercano(fantasma, fantasma.distanciaDeColision.doubleValue());
                
        }
        private Obstaculo probarSentidoHorizontalIzquierda (FantasmaBobo fantasma, ServicioJuego servicio) {
            fantasma.cambiarSentidoAHorizontalIzquierda();
            fantasma.x += fantasma.sentidoX;
            fantasma.y += fantasma.sentidoY;
            return servicio.obtenerObstaculoMasCercano(fantasma, fantasma.distanciaDeColision.doubleValue());
        }
    }

}
